\section{Реализация}

Контекстом данной работы является разработка метода задания принтеров с помощью шаблонов.
Под \emph{шаблоном} здесь и далее понимаются данные, сопоставляя которые с
переданным на печать синтаксическим деревом, мы будем получать текстовое представление
для этого дерева. Более точно, каждый конкретный шаблон позволяет построить
текстовые представления для подходящего по типу 
узла синтаксического дерева, используя уже полученные представления дочерних поддеревьев.

Фактически в реализации шаблон --- это размеченное дерево разбора
некоторой синтаксической конструкции, в котором по каждой метке можно выяснить
ограничения на представление соответствующих поддеревьев и описание, каким
образом нужная раскладка поддерева должна быть вставлена в шаблон.
При этом сами шаблоны получаются из некоторого эталонного репозитория с исходном
кодом на целевом языке определяемого принтера.
Так, к примеру, принтер будет использовать форматирование GNU, если ему на вход
передать один или несколько проектов в СК GNU.

Кроме того, полученный таким образом принтер должен выдавать оптимальное представление
для синтаксического дерева, переданного на печать. На нижнем уровне в принтере 
используются оптимальные принтер-комбинаторы с выбором. Здесь комбинатор выбора нужен
для возможности задания вариантов представления, соответствующих разным шаблонам.
Недостатком существующих оптимальных принтер-комбинаторов с выбором \cite{swierstra}
является их экспоненциальная сложность. Поэтому для возможности использования того же
комбинаторного интерфейса в рамках данной работы с помощью BURS была разработана
полиномиальная версия этих комбинаторов~\cite{podkopaevBoulytchev}.

Для апробации метода в случае языка Java был разработан принтер-плагин для
IntelliJ IDEA, что позволило переиспользовать синтаксический анализатор Java
для получения шаблонов.
Разработка велась на языке Kotlin.
Kotlin\footnote{\cd{http://kotlin.jetbrains.org/}}
--- это функциональный, объектно-ориентированный, компилируемый
в JVM-байткод и JavaScript язык, разрабатываемый компанией
JetBrains\footnote{\cd{http://jetbrains.org/}}.
Kotlin был выбран для реализации принтер-плагина к IntelliJ IDEA по нескольким
причинам. 
Во-первых, Kotlin обладает хорошей интеграцией с Java, что позволяет
использовать его с IDEA API. 
Во-вторых, функции в Kotlin являются объектами первого рода, что позволяет
легко реализовывать комбинаторные библиотеки на нем.
На данный момент Kotlin находится в стадии разработки, поэтому
периодически возникают
проблемы с тем, что исходные коды перестают быть совместимыми с новыми
версиями языка,
но обычно требуется внести небольшой набор исправлений для
восстановления работоспособности.

\input{bursReduction}

\newpage
\subsection{Расширение принтер-комбинаторов}

Для дальнейшего использования в принтер-плагине авторская библиотека комбинаторов
была переписана на язык Kotlin.
Эта реализация расширяет набор комбинаторов и использует
дополнительные техники, которые важны при практическом применении библиотеки в
контексте принтеров, задаваемых шаблонами.

\subsubsection{Мемоизация вычислений для поддеревьев}

В реальных принтерах документы, состоящие из комбинаторов
\lstinline[language=Haskell]{text},
\lstinline[language=Haskell]{indent},
\lstinline[language=Haskell]{beside},
\lstinline[language=Haskell]{above} и \lstinline[language=Haskell]{choice}, чаще всего
представляют собой не дерево, а \emph{дэг} (DAG, Direct Acyclic Graph). Поэтому при наивной
реализации можно получить экспоненциальную сложность от размеров документа,
поскольку соответствующее дерево будет иметь экспоненциальный размер от размера дэга.
Чтобы избежать этой проблемы достаточно завести ассоциативный массив,
который будет хранить посчитанный набор раскладок по конкретному поддереву.
Кроме того, при наивной реализации данной оптимизации
алгоритм вычисления раскладки становится квадратичным от размера дэга,
так как вычисление хэша, используемого ассоциативным массивом, для дерева занимает
линейное время. Чтобы преодолеть и эту сложность, в дереве хранится вычисленный хэш.

\subsubsection{Комбинатор \lstinline{fill}}

Для реализации принтера, использующего шаблоны, в библиотеку необходимо добавить
дополнительный принтер-комбинатор --- комбинатор \lstinline{fill}.

\begin{figure}[h!]
  \centering
	\subfloat[]{
		\centering
    \tikz[scale = 2.5]{
       \draw (0,0) -- (1,0) -- (1,0.2) -- (2,0.2) -- (2,1) --
          (0, 1) -- cycle;
       \draw (0,-0.1) -- (0,-1.1) -- (1.1,-1.1) -- (1.1,-0.9) --
          (2.3,-0.9) -- (2.3, 0.1) -- (1.1,0.1) -- (1.1,-0.1) -- cycle;
    }
		\label{fig:fill1}
	}
	\quad
  \subfloat[][fc --- константа сдвига]{
		\centering
    \tikz[scale = 2.5]{
          \draw (0,0) -- (1,0) -- (1,0.2) -- (2,0.2) -- (2,1)
            -- (0, 1) -- cycle;
          \draw (0.3,-0.1) -- (0.3,-1.1) -- (1.1,-1.1) -- (1.1,-0.9)
            -- (2.3,-0.9) -- (2.3, 0.1) -- (1.1,0.1) -- (1.1,-0.1) -- cycle;
          \draw[<->] (0,-0.6) -- (0.3,-0.6);
          \draw (0.15,-0.4) node [fill=white] {\tiny fc};
          \draw[dashed] (0,0) -- (0,-1.1);
       }
		\label{fig:fill2}
	}
	\caption{Оператор Fill}
\end{figure}

В качестве примера, показывающего необходимость такого комбинатора, рассмотрим
печать метода из языка Java. Пусть нам нужно получить на выходе следующий программный
код:

\lstinputlisting[language = Java]{codes/codeBlockEx.java}

По описанному раннее подходу, для построения общей раскладки необходимо иметь представления
поддеревьев. В частности, представление тела метода:

\lstinputlisting[language = Java]{codes/justBlockEx.java}

Тогда для реализации соответствующего соединения, представленного ниже,
необходим комбинатор \lstinline{fill}:

\lstinputlisting[language = Java]{codes/codeBlockTemplateEx.java}

К сожалению, введение нового комбинатора накладывает дополнительные ограничения на
факторизацию. Это связано с тем, что теперь по парам $(n, k)$, где $n$ --- общая ширина,
а $k$ --- ширина последней строчки, нельзя однозначным образом выдать соответствующую
пару для раскладки соединения двух блоков. Так, при применении \lstinline{fill}-комбинатора
важной становится и ширина первой строчки нижнего блока, поскольку сумма ее и ширины
последней строчки верхнего блока могут дать максимум общей ширины. Таким образом,
у нетерминалов из BURS-сведения появляется дополнительный индекс, а их число
увеличивается с $w^2$ до $w^3$. Аналогично, количество правил перехода возрастает
с $O(w^4)$ до $O(w^6)$.

\subsubsection{Дополнительная фильтрация вариантов}

Для того, чтобы избежать экспоненциальной сложности вычисления оптимальной
раскладки документа, мы ввели факторизацию по размерам (ширинам) текстового
представления.
Количество раскладок для каждого поддерева теперь ограничено, но, к сожалению,
слишком большой константой --- $w^3$ ($w^2$ для набора комбинаторов без
\lstinline{fill}), а сложность вычисления узлов
\lstinline{beside}, \lstinline{above}, \lstinline{fill} --- $O(w^6)$
($O(w^4)$ без комбинатора \lstinline{fill}). Факторизация была основана на том факте, что
среди раскладок с одинаковыми ширинами в любом дальнейшем использовании
к лучшему результату приводит вариант с меньшей высотой.
Для дополнительной фильтрации множества
вариантов воспользуемся рассуждением, что если раскладка $A$ обладает и
меньшими ширинами, и меньшей высотой, чем раскладка $B$, то, аналогично
предыдущей факторизации,
достаточно оставить только ее во множестве вариантов.

Таким образом мы ввели отношение частичного порядка на раскладках, а
описанная фильтрация --- поиск минимумов на частично упорядоченном
множестве представлений\cite{poset}.
Очевидная реализация поиска имеет квадратичную сложность от размера множества,
таким образом асимптотика алгоритма не ухудшается из-за данной фильтрации.
К сожалению, эта оптимизация и не улучшает поведение
в худшем случае, так как
можно привести пример, когда все множество раскладок с допустимыми ширинами
будет состоять из несравнимых элементов, но на практике количество вариантов
существенно сокращается, что делает весь подход применимым на реальных данных.

\subsubsection{Вставка в шаблон}
\label{txt:templateInsert}

В шаблонном подходе результат печати для узла синтаксического дерева строится
за счет вставки представлений его поддеревьев в текст шаблона.
Рассмотрим построение раскладки для конструкции
\lstinline{if}. У этой конструкции в общем случае три поддерева: условие,
ветка, соответствующая выполнению условия, и ветка, соответствующая
невыполнению условия.
На рис.~\ref{fig:ifEx} представлен пример шаблона и представлений поддеревьев для
конструкции \lstinline{if}.

\begin{figure}[h!]
  \subfloat[Шаблон]{
    \makebox[0.15\textwidth]{
      \centering
      \lstinputlisting[language = Java]{codes/ifTemplate.java}
    }
    \label{fig:ifTemplate}
  }
  \quad
  \subfloat[Условие]{
    \makebox[0.15\textwidth]{
      \centering
      \lstinputlisting[language = Java]{codes/ifCond.java}
    }
    \label{fig:ifCond}
  }
  \subfloat[Первая ветка]{
    \makebox[0.3\textwidth]{
      \centering
      \lstinputlisting[language = Java]{codes/ifThen.java}
    }
    \label{fig:ifThen}
  }
  \subfloat[Вторая ветка]{
    \makebox[0.3\textwidth]{
      \centering
      \lstinputlisting[language = Java]{codes/ifElse.java}
    }
    \label{fig:ifElse}
  }

  \caption{Пример входных данных для построения представления конструкции \lstinline{if}}
  \label{fig:ifEx}
\end{figure}

В шаблоне места для поддеревьев выделены блоками. Свойства этих блоков задают
ограничения на использование представлений соответствующих поддеревьев.
Так, в данном случае для каждого из них верно,
что представление, применяемое для вставки, может быть многострочным. Блок, связанный
с поддеревом условия, помимо этого также задает использование комбинатора
\lstinline{fill} с константой сдвига, соответствующей положению начала
второй строчки блока относительно родительского узла.
При использовании шаблона и представлений
поддеревьев с рис.~\ref{fig:ifEx} получается следующий результат:

%\begin{figure}[h!]
  \lstinputlisting[language = Java]{codes/ifResult.java}
 % \caption{Результат вставки в шаблон}
  %\label{fig:templateResult}
%\end{figure}

При наивной реализации сложность вставки в шаблон есть
$O(w^{3\times n})$, где $w$ --- максимальная ширина вывода,
а $n$ --- число дочерних поддеревьев обрабатываемого узла,
поскольку каждое из поддеревьев может иметь $w^3$ различных
представлений после факторизации.
Однако, за счет инкрементальной реализации вставки
можно получить сложность $O(n \times w^6)$.
Для этого надо разбить шаблон на список, в котором элементы соответствуют
либо месту вставки поддерева, либо независимому тексту шаблона. Так, шаблон
с рис.~\ref{fig:ifTemplate} представляется списком:
\begin{itemize}
  \item ``\lstinline{if (}'';
  \item место условия;
  \item ``) \{'';
  \item место первой ветки;
  \item ``\} \lstinline{else} \{'';
  \item место второй ветки;
  \item ``\}''.
\end{itemize}

После над этим списком можно произвести свертку. При этом к аккумулятору на каждом
шаге будет присоединяться представление нового элемента списка с помощью
комбинаторов \lstinline{beside}, \lstinline{above} или \lstinline{fill}. 
В таком случае аккумулятор всегда будет содержать не более
$w^3$ представлений, как и каждый элемент обрабатываемого списка.
Поэтому операция присоединения
на каждом шаге будет иметь сложность $O(w^6)$, а вся вставка ---
$O(n \times w^6)$, так как количество участков текста не более чем $n + 1$, а значит
списочное представление шаблона имеет размер не более $2 \times n + 1$.

\newpage
\subsection{Принтер-плагин языка Java для IntelliJ IDEA}

Работа принтер-плагина разбивается на два этапа: подготовка шаблонов и непосредственно
печать синтаксического дерева.

Для получения шаблонов алгоритм проходит по переданному пользователем репозиторию с
эталонным исходным кодом. По каждому узлу деревьев разбора файлов из репозитория
строится шаблон соответствующей синтаксической конструкции. Важной особенностью
метода в целом
является то, что для дальнейшей печати конструкций, имеющих плавающие число
поддеревьев, необходимо иметь отдельный шаблон для каждой комбинации поддеревьев.
Так, к примеру, для конструкции \lstinline[language = Java]{if} языка Java
необходимо иметь не менее двух шаблонов --- с веткой \lstinline[language = Java]{else}
и без нее. В данном случае такое требование не кажется слишком обременительным, но
если рассмотреть случай Java-класса, то ситуация несколько хуже --- у него может не
быть списка модификаторов доступа, суперкласса и реализуемых интерфейсов.
Это серьезное ограничение, но его невозможно обойти в рамках данного подхода,
поскольку единственной информацией о представлении конструкций должны быть
полученные шаблоны.

Вычисление текстового представления переданного на вход синтаксического
дерева происходит путем восходящего переписывания. Для каждого узла строится множество
возможных раскладок (набор блоков текста) по тем же правилам факторизации, чтобы были
применены для комбинаторной библиотеки. В дальнейшем представления поддеревьев
используются для подстановки в шаблоны родительского узла, что на выходе дает
набор раскладок для него. Шаблоны в данном контексте можно рассматривать как
BURS-правила.
Однако, несмотря на то, что число детей у родительского узла может
быть велико (в случае некоторых конструкций языка Java более 5),
это не приводит к экспоненциальному росту сложности по ширине вывода, которые
следуют из рассуждений раздела \ref{txt:bursReduction}, благодаря технике, описанной
в разделе \ref{txt:templateInsert}. Эта техника позволяет проводить подстановку
раскладок за 
$O(n \times w^{6})$ для любого $n \geq 2$ ($O(w^{3})$ для $n = 1$), где
$w$ --- ширина вывода, а $n$ --- число дочерних поддеревьев обрабатываемого узла.
Таким образом, сложность вычисления сушественных раскладок для узла дерева
составляет $O(t \times n \times w^{6})$, где $t$ --- общее число шаблонов,
а в целом поиск оптимального представления всего синтаксического дерева, переданного
на форматирование, занимает $O(m \times t \times n \times w^{6})$, где $m$
--- количество узлов в дереве.


Стоит заметить, что с инженерной точки зрения для решения отдельно стоящей задачи
форматирования по образцу кода на Java в IntelliJ IDEA лучше подходит
получение настроек для существующего форматтера с помощью известных техник обучения.
Подобный подход используется в работе \cite{learning}.
Но описанный в данной работе метод позволяет выразить представления, которые не
предусмотрены встроенным форматтером, кроме того потенциально предоставляет
возможность проще создавать принтеры для новых языков. 

\subsubsection{Практические особенности получения и использования шаблонов}

Поскольку каждый узел дерева разбора из эталонного репозитория порождает
шаблон, то частой ситуацией является присутствие шаблонов, которые функционально
неотличимы. Их наличие ухудшает время работы принтера, так как повторяются
аналогичные переходы от представлений поддеревьев обрабатываемого узла, приводящие
к одному и тому же результату. Чтобы избежать данной проблемы достаточно
отождествить шаблоны, обладающие одинаковым функциональном поведением.
На практике это было реализовано в виде факторизации по текстовому представлению
шаблона, в котором на месте меток записана информация об ограничении на раскладки
соответствующих поддеревьев.

Противоположную проблему создают шаблоны, получаемые из уникальных,
встречаемых только в специальном контексте представлений.
Так, рассмотрим шаблоны для поля класса Java,
получаемые из следующего текста:

\lstinputlisting[language = Java]{codes/fieldsT1.java}

Здесь на выходе получается один и тот же факторизованный шаблон.
Несколько изменим пример. Пусть теперь
в эталонном коде было сделано дополнительное выравнивание по типам
и модификаторам доступа:

\lstinputlisting[language = Java]{codes/fieldsT2.java}

По такому эталону получается три разных шаблона, каждый из которых не может
быть использован в произвольном случае. Кроме того, так как подход в целом не
умеет представлять поддеревья в зависимости от контекста, то полученные шаблоны
бесполезны и вредны. Поэтому для борьбы с подобными ситуациями имеет смысл
предпочитать то представление, что было получено из факторизованного шаблона с
наибольшим \emph{весом}, где под весом понимается встречаемость шаблона в
эталонном тексте.

\newpage
\subsubsection{Обработка списочных структур}

Особую сложность для представленного метода имеет
печать синтаксических структур с переменным, неограниченным числом
подвыражений. К примеру, это списки и бинарные выражения. Проблема вызвана
неясностью в том, как для таких структур задавать шаблоны. Можно хранить
представления для списков по количеству элементов
до некоторой максимальной длины, но,
во-первых, это достаточно обременительно, учитывая количество разнообразных
типов списков, а, во-вторых, такой подход не сможет работать на списках большей
длины. В рамках данной работы было реализовано два разных решения для этой
задачи: классический, применяемый форматтерами из IDE, и с использованием
шаблона для пары элементов списка.

Классический способ решения проблемы заключается в том, что
списки просто печатаются заполняющим методом (см. рис.~\ref{fig:listFill}),
то есть перевод строки происходит
в случае, когда следующий элемент уже не помешается на строчку, или каждый
новый элемент печатается на новой строчке (см. рис.~\ref{fig:listEveryLine}).
Кроме того, обычно позиция разделителя относительно элемента списка жестко
``зашита'' в форматтер --- к примеру,
запятая в списке параметров функции идет сразу же за параметром.
Этот метод явным образом выбивается из модели шаблонов, так как
вместо информации, предоставляемой эталонным кодом, использует наши
ожидания о представлении списков, которые не всегда применимы,
несмотря на то, что для большинства реальных ситуаций данное форматирование
соответствует СК.
Достоинством подхода является простота его реализации.

\begin{figure}[h!]
  \subfloat[Заполняющее форматирование списков]{
    \lstinputlisting[language = Java]{codes/listEx1.java}
    \label{fig:listFill}
  }
  \subfloat[Форматирование списка с переводом строки на каждый элемент]{
    \lstinputlisting[language = Java]{codes/listEx2.java}
    \label{fig:listEveryLine}
  }
  \caption{Пример форматирования списков в IDE}
\end{figure}

Другое решение заключается в использовании специального шаблона, который
задает переход между представлением головы списка и следующим обрабатываемым
элементом. По сути метод является сверткой списка, где начальным значением
является множество представлений первого элемента, а функция перехода с помощью
шаблона соединяет
посчитанные раскладки и результат форматирования следующего элемента.
Данный подход позволяет получить нестандартные способы форматирования
из эталонного кода (см. рис.~\ref{fig:listGoodLine}).

\begin{figure}[h!]
  \lstinputlisting[language = Java]{codes/listEx3.java}
  \caption{Пример нестандартного форматирования списков}
  \label{fig:listGoodLine}
\end{figure}

На самом деле, одного шаблона перехода 
в большинстве ситуаций недостаточно,
так как получаемое представление списка может стать слишком широким и не попасть
в ограничение (в случае, если шаблон \emph{``горизонтальный''}).
Так, необходимо использовать согласованную группу шаблонов
(см. рис.~\ref{fig:tmpltGroup}), которая
позволит реализовывать некоторое обобщение заполняющего метода.
Недостатком такого способа решения является сложность в идентификации подобных
групп шаблонов, а также в сильно возрастающем числе самих шаблонов при наивном
использовании всех переходов из эталонного кода.

\begin{figure}[h!]
  \centering
  \subfloat[``Горизонтальный'' шаблон]{
    \makebox[0.4\textwidth]{
      \lstinputlisting[language = Java]{codes/horListEx.java}
    }
  }
  ~
  \subfloat[``Вертикальный'' шаблон]{
    \makebox[0.4\textwidth]{
      \lstinputlisting[language = Java]{codes/vertListEx.java}
    }
  }
  \caption{Группа шаблонов для списков}
  \label{fig:tmpltGroup}
\end{figure}

Кроме того, некоторые стили
форматирования списков могут быть сильно специфичными для мест использования.
В частности, форматирование списка реализуемых интерфейсов Java-класса может
концептуально зависеть от шаблона, используемого для форматирования всего класса
в целом. Эта специфика приводит к усложнению общего алгоритма в смысле реализации
и алгоритмически, так как для каждого узла дерева, переданного на печать,
надо хранить не только единый набор раскладок, но и оптимальные
множества представлений для каждого шаблона более высокого уровня.

% (см. пример на
% рисунке~\ref{fig:listClassicEx}).

% \begin{figure}[h!]
%   \textbf{Тут надо привести какой-нибудь пример}

%   \caption{Пример классической печати списков}
%   \label{fig:listClassicEx}
% \end{figure}

\newpage
\subsubsection{Обработка комментариев}

Отдельно стоит отменить задачу обработки комментариев. В целом, комментарии
существенно выбиваются из общей картины. Первой их особенностью является то,
что обычные неструктурированные комментарии, которые не
предназначены для автоматической обработки подобно
JavaDoc\footnote{\cd{http://java.sun.com/j2se/javadoc/}}
и Doxygen\footnote{\cd{http://doxygen.org/}},
невозможно видоизменить, так как для этого необходимо понимать семантику
комментария. Вторая особенность заключается в том, что для комментариев нет
специально отведенных мест в абстрактном синтаксическом дереве программы
(в соответствии с некоторыми определениями, комментариев в нем вообще быть не может),
а значит для подхода, ориентированного на структурное синтаксическое сравнение, они
являются специальным случаем.

В рамках данной работы было применен следующий подход: каждый блок комментариев
связывается с узлом синтаксического дерева. Для связывания выбирается узел, который
ближе всего к комментарию, при этом комментарий лежит полностью в его родительском узле.
После построения вариантов раскладки для выбранного узла, к каждому из них
присоединяется комментарий сверху или снизу, в зависимости от изначального положения.
Получившееся множество представлений считается ответом для выбранного узла.

Существенным недостатком того, что комментарии остаются без изменений,
является то, что нельзя управлять их шириной, что может привести к нежелательному
увеличению минимальной возможной ширины раскладки всего дерева.
Так, наличие строки комментария в
100 символов не дает возможности отформатировать текст на меньшую ширину.
В частности, это объясняет, почему в нижеследующем анализе производительности для
форматирования используются столь большие ширины (200 и 250). Возможным решением
этой проблемы может быть смягчение требования на ширину текста --- разрешение
комментариям выходить за границы.

Дополнительным артефактом приведенного алгоритма обработки комментариев является то,
что результат работы
принтер-плагина не является его неподвижной точкой, поскольку при повторном запуске
комментарии могут быть присоединены к другим синтаксическим узлам, нежели при предыдущем
запуске.

Также комментарии влияют на получение шаблонов:
наличие комментария внутри или рядом с синтаксической структурой
может серьезно менять форматирование в эталонном коде, поэтому от большинства
шаблонов, содержащих комментарии, приходится отказываться.

\newpage
\subsubsection{Анализ производительности}

Для оценки производительности были рассмотрены средние и большие исходные файлы
проекта IntelliJ IDEA Community
Edition\footnote{\cd{http://github.com/jetbrains/intellij-community/}}.
Средние файлы проекта имеют размер от 100-500 строк, большие --- несколько тысяч строк.
Результаты производительности для средних и больших файлов приведены в
таблице~\ref{tbl:pluginPerformanceTbl}.

\begin{table}[h!]
	\centering

  % \subfloat[Ширина 200] {
    % \centering
    \begin{tabular}{c c c c}
    Имя файла & Кол-во строк & Время (шир. 200) & Время (шир. 250) \\
    \hline
    SearchRequestCollector.java & 169 & 0.04 & 0.05\\
    XDebugProcess.java & 236 & 0.02 & 0.02\\
    InitialConfigurationDialog.java & 455 & 0.11 & 0.11 \\
    QuickEditHandler.java & 504 & 0.21 & 0.21\\
    PsiDirectoryImpl.java & 618 & 0.11 & 0.12\\
    \hline
    Messages.java & 2007 & 0.73 & 0.74 \\
    UIUtil.java & 2808 & 1.15 & 1.14 \\
    AbstractTreeUi.java & 5112 & 1.62 & 2.01 \\
    EditorImpl.java & 6789 & 2.07 & 2.09\\
    ConcurrentHashMap.java & 7191 & 1.38 & 1.39
    \end{tabular}
  % }

  \caption{Время форматирования файлов (в секундах)}
	\label{tbl:pluginPerformanceTbl}
\end{table}

Эти данные показывают, что принтер может быть использован для
форматирования малых и средних файлов, но на больших файлах время
работы слишком велико для практического применения. Проблему производительности
можно будет считать решенной, если время работы снизится до 0.5 секунд.

\subsubsection{Открытые проблемы}

Проблемой, которую не получилось решить в рамках данной работы,
является задание порядка поддеревьев для конструкций, которые
его явным образом не определяют. К примеру, это верно для классов в Java:
часто порядок полей, методов и подклассов может быть произвольным в контексте
семантики программы, но в большинстве СК описаны рекомендации для их размещения
относительно друг друга. В форматтерах IDE данная проблема решается просто ---
среди настроек есть указание порядка подконструкций для подобных случаев.
В шаблонах это свойство явно не задается. Кроме попытки использования
уже существующего подхода, данную проблему также можно 
решать с помощью обучения на эталонном репозитории или с дополнительным
конфигурационным файлом. Но в целом такая задача несколько выбивается из
простой печати синтаксического дерева, так как изменение порядка, к примеру,
Java-полей может привести к изменению семантики программы, поэтому для проведения
упорядочивания элементов конструкций может требоваться дополнительный статический
анализ.
